/* ****************************************************************************
 *
 * Copyright (c) Microsoft Corporation. 
 *
 * This source code is subject to terms and conditions of the Microsoft Public License. A 
 * copy of the license can be found in the License.html file at the root of this distribution. If 
 * you cannot locate the  Microsoft Public License, please send an email to 
 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
 * by the terms of the Microsoft Public License.
 *
 * You must not remove this notice, or any other, from this software.
 *
 * THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
 * PARTICULAR PURPOSE.
 * ***************************************************************************/


#include "stdafx.h"
#include <pgl/core/PGLLine.h>
#include <pgl/core/PGLLinePropPage.h>
#include <pgl/core/PGLCircleMarker.h>
#include <pgl/core/PGLPolygonMarker.h>
#include <pgl/core/PGLCircPolygonMarker.h>
#include <pgl/core/PGLCharMarker.h>

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

PGL_IMPLEMENT_DYNCREATE(CPGLLine, CPGLObject);
PGL_IMPLEMENT_SCRIPTING(Line);

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

//! Save settings
void CPGLLine::SerializeXML(CPGLArchive& ar)
{
	static TCHAR szTag[] = _T("CPGLLine");
	CString strPos;	strPos.Format(_T("linePos%u"),(DWORD)this);

	CString str;
	markup::CMarkupArchive& xml = *((markup::CMarkupArchive*)ar.GetMarkup());
	if(ar.IsStoring())
	{
		xml.AddChildElem(szTag);
		xml.IntoElem();

			/////////////////////////////////////////////////////////
			// Adding base class
			CPGLObject::SerializeXML(ar);
			
			/////////////////////////////////////////////////////////////////////////////
			// adding line characteristics
			xml.AddChildElem(_T("LineStrip"));
			xml.IntoElem();
				xml.AddChildElemEx(_T("Interpolation"),InterpolationTypeToString(m_eInterpolationType));	
				xml.AddChildElemEx(_T("Tension"),m_dTension);
				xml.AddChildElemEx(_T("DashPattern"),m_sDashPattern);
				// adding line width
				xml.AddChildElemEx(_T("Width"),m_fLineWidth);
			xml.OutOfElem();
		
			/////////////////////////////////////////////////////////////////////////
			// points
			xml.AddChildElem(_T("Marker"));
			xml.IntoElem();
				if (m_pMarker)
					m_pMarker->SerializeXML( ar );
				xml.AddChildElemEx(_T("ColorSynchro"), m_bMarkerColorSynchro);
			xml.OutOfElem();

			//////////////////////////////////////////////////////////////////////////
			// the rest
			xml.AddChildElemEx(_T("StripSize"),m_iStripSize);
			xml.AddChildElemEx(_T("Contoured"),m_bContour);
			xml.AddChildElemEx(_T("Filled"),m_bFilled);
			xml.AddChildElemEx(_T("Open"),m_bOpen);
			xml.AddChildElemEx(_T("SnapBottom"),m_bSnapBottom);
			xml.AddChildElemEx(_T("Shadow"),m_bShadow);

		xml.OutOfElem();
	}
	else
	{
		if  (!xml.FindChildElem(szTag))
				return;

		xml.IntoElem();

			/////////////////////////////////////////////////////////
			// loading base class
			CPGLObject::SerializeXML(ar);
		
			/////////////////////////////////////////////////////////////////////////////
			// adding line characteristics
			if (xml.FindChildElem(_T("LineStrip")))
			{
				xml.IntoElem();
					if (xml.FindChildElem(_T("Interpolation")))
					{
						m_eInterpolationType=StringToInterpolationType(xml.GetChildData());
					}
					xml.FindGetChildData(_T("Tension"), m_dTension);
					xml.FindGetChildData(_T("DashPattern"), m_sDashPattern);
					// adding line width
					xml.FindGetChildData(_T("Width"),m_fLineWidth);

				xml.OutOfElem();
			}
			

			/////////////////////////////////////////////////////////////////////////
			// points
			if (xml.FindChildElem(_T("Marker")))
			{
				bool markerFound=false;
				CPGLNodePtr pObject;
				xml.IntoElem();
					xml.ResetChildPos();
					VERIFY(xml.SavePos(strPos));
					while(xml.FindChildElem() && !markerFound)
					{
						pObject=pgl::CObjectRegistrySingleton::GetObject(xml.GetChildTagName());
						if (pObject)
						{
							if (pObject->IsKindOf(RUNTIME_CLASS(CPGLMarker)))
							{
								m_pMarker=boost::shared_polymorphic_cast<CPGLMarker, CPGLNode> (pObject);
								VERIFY(xml.RestorePos(strPos));
								m_pMarker->SerializeXML(ar);

								markerFound=true;
							}
						}
					}


					xml.FindGetChildData(_T("ColorSynchro"), m_bMarkerColorSynchro);
				xml.OutOfElem();
			}
		

			//////////////////////////////////////////////////////////////////////////
			// the rest
			xml.FindGetChildData(_T("StripSize"),m_iStripSize);
			xml.FindGetChildData(_T("Contoured"),m_bContour);
			xml.FindGetChildData(_T("Filled"),m_bFilled);
			xml.FindGetChildData(_T("Open"),m_bOpen);
			xml.FindGetChildData(_T("SnapBottom"),m_bSnapBottom);
			xml.FindGetChildData(_T("Shadow"),m_bShadow);

		xml.OutOfElem();

		m_bMarkerNeedUpdate=true;
	}
}

#ifdef _DEBUG
void CPGLLine::Dump( CDumpContext& dc ) const
{
    // call base class function first
    CPGLObject::Dump( dc );

    // now do the stuff for our specific class
	dc << _T("CPGLLine ID ") << GetID() << "\n";
}
void CPGLLine::AssertValid() const
{
    // call inherited AssertValid first
    CPGLObject::AssertValid();

    // check members...
	ASSERT(m_fLineWidth>=0);
} 
#endif

CPGLLine::CPGLLine(const CPGLColor& _FrontColor, const CPGLColor& _FillColor, EInterpolationType eInterpolationType, 
				   EMarkerType _eMarkerType,LPCTSTR _szDashPattern)
: CPGLObject(_FrontColor, _FillColor), 
m_eInterpolationType(eInterpolationType), m_sDashPattern(_szDashPattern),
m_dTension(0), 
m_fLineWidth(1.5),
m_iStripSize(2),
m_bSnapBottom(false),
m_bContour(true),
m_bFilled(false),
m_bOpen(true),
m_bShadow(false),
m_bMarkerColorSynchro(true)
{
	SetMarkerType( _eMarkerType);
	PreparePropertyMap();
}

CPGLLine::~CPGLLine()
{
	CleanMarker();
}

void CPGLLine::CleanMarker()
{
	m_pMarker.reset();
}


CPGLLine& CPGLLine::operator = (const CPGLLine& l)
{
	// prevent self-assignement
	if (&l != this)
	{
		// invoke CPGLObject copy assignement operator
		this->CPGLObject::operator =(l);

		// copy the rest
		m_eInterpolationType=l.m_eInterpolationType;
		m_dTension = l.m_dTension;
		m_sDashPattern=l.m_sDashPattern;
		m_fLineWidth=l.m_fLineWidth;
		m_iStripSize = l.m_iStripSize;
		m_bSnapBottom=l.m_bSnapBottom;
		m_bContour=l.m_bContour;
		m_bFilled=l.m_bFilled;
		m_bOpen=l.m_bOpen;
		m_bShadow=l.m_bShadow;

		m_pMarker.reset();
		if (l.m_pMarker)
			m_pMarker = boost::shared_polymorphic_cast<CPGLMarker, CPGLNode>(l.m_pMarker->MakeCopy());
	}
	return *this;
}

void CPGLLine::AddContextMenuItems(CMenu* pMenu)
{
	// call base class
	CPGLObject::AddContextMenuItems(pMenu);

	// add own items
}

void CPGLLine::AddPropertyPage(CPropertySheet *pPropSheet)
{
	// call own functions
	CPGLLinePropPage* propPage=new CPGLLinePropPage(this);
	pPropSheet->AddPage(propPage);

	// first call base class function
	CPGLObject::AddPropertyPage(pPropSheet);
}

void CPGLLine::PlotGfx(gfxinterface::CGfxInterface& gfx)
{
	if (!IsVisible())
		return;

	if (!IsInLayer(gfx.GetLayer()))
		return;

	// calling base class plot
	CPGLObject::PlotGfx(gfx);	
	
	//updating if snap to bottom...
	if (m_bFilled && m_bSnapBottom)
	{
		CPGLRectD rBBox = GetExtent();
		rBBox.bottom = gfx.GetWorldBottom();

		GetFillBrush()->SetBBox(rBBox);
		GetFillBrush()->MakeCurrentFill(gfx);
	}

	GFX_ADDCOMMENT(gfx,"--- CPGLLine ---");	

	gfx.SetLineWidth(GetLineWidth());	
	gfx.PushState();
		SetDashGfx(gfx);	
		// set line width
		PlotLineStripGfx(gfx);
	gfx.PopState();



	if (m_pMarker && m_pMarker->IsVisible())
	{
		ASSERT(m_pMarker);
		if (m_bMarkerNeedUpdate)
			UpdateMarker();

		PlotPointStripGfx(gfx);
	}

	PlotArrowsGfx(gfx);
}

void CPGLLine::PlotLegendGfx(gfxinterface::CGfxInterface& gfx, const CPGLRectD& rBox)
{
	if (!IsVisible())
		return;

	CPGLBrushPtr pBrush;

	if (!GetFillBrush())
		pBrush.reset();
	else
		pBrush = boost::shared_polymorphic_cast<CPGLBrush, CPGLNode>(GetFillBrush()->MakeCopy());

	// calling base class plot
	CPGLObject::PlotGfx(gfx);

	gfx.SetLineWidth(m_fLineWidth);
	gfx.PushState();
		DWORD lineType = GetELineType();
		// no shadowing
		lineType= ~(~lineType | gfxinterface::LineTypeShadow);

		SetDashGfx(gfx);
		CPGLVectorF vX;
		CPGLVectorF vY;
		if ( (!IsOpen() && ! IsSnapBottom())
			|| (IsFilled() && ! IsSnapBottom()) )
		{
			vX.resize(4);
			vY.resize(4);
			vX[0]=(float)rBox.left; vX[1]=(float)rBox.right;
			vX[2]=(float)rBox.right; vX[3]=(float)rBox.left;
			vY[0]=vY[1]=(float)(rBox.bottom+rBox.GetHeight()*0.5);
			vY[2]=vY[3]=(float)rBox.bottom;
			if (pBrush)
			{
				pBrush->SetBBox( rBox );
				pBrush->MakeCurrentFill(gfx);
			}
			gfx.DrawLineStrip(vX, vY, lineType);
		}
		else
		{
			vX.resize(2);
			vY.resize(2);
			vX[0]=(float)rBox.left; vX[1]=(float)rBox.right;
			vY[0]=vY[1]=(float)(rBox.bottom+rBox.GetHeight()*0.5);
			if (pBrush)
			{
				pBrush->SetBBox( CPGLRectD(rBox.left, rBox.bottom, rBox.right, vY[1]) );
				pBrush->MakeCurrentFill(gfx);
			}
			gfx.DrawLineStrip(vX, vY, lineType);
		}
	gfx.PopState();

	if (m_pMarker && m_pMarker->IsVisible())
	{
		gfx.PushState();
			double x=rBox.left+rBox.GetWidth()/2.0;
			double y=rBox.bottom+rBox.GetHeight()/2.0;	
			m_pMarker->PlotLegendGfx(gfx,(float)x,(float)y);
		gfx.PopState();
	}

}

void CPGLLine::SetDashGfx(gfxinterface::CGfxInterface& gfx)
{
	gfx.SetDash(m_sDashPattern);
}

DWORD CPGLLine::GetELineType() const
{
	using namespace gfxinterface;
	DWORD lt;

	if (m_bContour)
		lt=0;
	else
		lt=LineTypeNoContour;

	if ( ! m_bOpen )
		lt |= LineTypeClosed;
	if (m_bSnapBottom)
		lt |= LineTypeSnapBottom;
	if (m_bFilled)
		lt |= LineTypeFilled;
	if (m_bShadow)
		lt |= LineTypeShadow;

	return lt;
}

DWORD CPGLLine::GetERectType() const
{
	using namespace gfxinterface;
	DWORD lt;

	if (m_bContour)
		lt=0;
	else
		lt=RectTypeNoContour;

	if (m_bFilled)
		lt |= RectTypeFilled;

	return lt;
}

void CPGLLine::SetLineState(gfxinterface::CGfxInterface &gfx)
{
	// calling base class plot
	CPGLObject::PlotGfx(gfx);
	// setting dashing
	SetDashGfx(gfx);	
	// set line width
	gfx.SetLineWidth(GetLineWidth());	
}

CString CPGLLine::InterpolationTypeToString(EInterpolationType eInterpolationType)
{
	switch(eInterpolationType)
	{
	case InterpolationTypeStep:
		return CString(_T("step"));
	case InterpolationTypeSpline:
		return CString(_T("spline"));
	case InterpolationTypeSegment:
		return CString(_T("segment"));
	default:
		return CString(_T("linear"));
	}
}

CPGLLine::EInterpolationType CPGLLine::StringToInterpolationType(LPCTSTR szInterpolationType)
{
	CString str(szInterpolationType); 
	str.MakeLower();

	if (str==_T("step"))
		return InterpolationTypeStep;
	else if (str == _T("spline"))
		return InterpolationTypeSpline;
	else if (str == _T("segment"))
		return InterpolationTypeSegment;
	else return InterpolationTypeLinear;

}

void CPGLLine::SetMarker(CPGLMarkerPtr pMarker)
{	
	m_pMarker = pMarker; 
	m_bMarkerNeedUpdate=true;
};

//! Sets the marker type (obsolete)
void CPGLLine::SetMarkerType(EMarkerType eMarkerType)
{
	CPGLCircleMarkerPtr pCircleMarker;
	CPGLCircPolygonMarkerPtr pCircPolygonMarker;

	m_pMarker.reset();
	switch(eMarkerType)
	{
	case MarkerTypeContour:
		pCircleMarker=CPGLCircleMarkerPtr(new CPGLCircleMarker);
		m_pMarker=pCircleMarker;
		m_pMarker->Show();
		break;

	case MarkerTypeCircle:
		pCircleMarker=CPGLCircleMarkerPtr(new CPGLCircleMarker);
		pCircleMarker->SetFilled(true);
		m_pMarker=pCircleMarker;
		m_pMarker->Show();
		break;

	case MarkerTypeTriangle:
		pCircPolygonMarker = CPGLCircPolygonMarker::GetTriangle();
		m_pMarker=pCircPolygonMarker;
		m_pMarker->Show();
		break;

	case MarkerTypeSquare:
		pCircPolygonMarker = CPGLCircPolygonMarker::GetSquare();
		m_pMarker=pCircPolygonMarker;
		m_pMarker->Show();
		break;
	}

	m_bMarkerNeedUpdate=true;
};

void CPGLLine::UpdateMarker()
{
	if (m_pMarker && m_bMarkerColorSynchro)
	{
		m_pMarker->SetColor( GetColor());
		m_pMarker->SetBrush( GetFillBrush() );
	}

	m_bMarkerNeedUpdate = false;
}


void CPGLLine::PreparePropertyMap()
{
	using namespace std;
	static bool bLoaded(false);
	
	if (bLoaded)
		return;
	bLoaded=true;

	CPGLObject::PreparePropertyMap();

	m_mProps[_T("Line.width")]=PropertyPair(LinePropWidth, _T("Width: pixels"));
	m_mProps[_T("Line.dash")]=PropertyPair(LinePropDash, _T("Dash: string (dash pattern)"));
	m_mProps[_T("Line.marker")]=PropertyPair(LinePropMarker, _T("Marker: Sets the marker type: point, circle, triangle, square, pentagon, hexagon, heptagon, ngon, string, custom"));
	m_mProps[_T("Line.open")]=PropertyPair(LinePropOpen, _T("Open: true - false"));
	m_mProps[_T("Line.contour")]=PropertyPair(LinePropContour, _T("Contour: true - false"));
	m_mProps[_T("Line.snapbottom")]=PropertyPair(LinePropSnapBottom, _T("Snap to bottom: true - false"));
	m_mProps[_T("Line.filled")]=PropertyPair(LinePropFilled, _T("Filled: true - false"));
	m_mProps[_T("Line.shadow")]=PropertyPair(LinePropShadow, _T("Shadow: true - false"));
	m_mProps[_T("Line.interpolation")]=PropertyPair(LinePropInterpolation, _T("Interpolation: linear - step - spline - multistrip"));
	m_mProps[_T("Line.stripsize")]=PropertyPair(LinePropStripSize, _T("Multi Strip size: int"));
	m_mProps[_T("Line.tension")]=PropertyPair(LinePropTension, _T("Spline tension: float"));
}

bool CPGLLine::SetProperty( LPCTSTR szProp, LPCTSTR szValue)
{
	if (CPGLObject::SetProperty(szProp, szValue))
		return true;

	CString sv(szValue);
	sv.MakeLower();

	UINT u;
	float f;
	switch( StringToLineProp( szProp ))
	{
	case LinePropWidth:
		if( _stscanf( sv, _T("%g"), &f ) )
			SetLineWidth(f); 
		return true;
	case LinePropDash:
		SetDashPattern( szValue );
		return true;
	case LinePropMarker:
		CleanMarker();
		if (sv==_T("point"))
		{
			m_pMarker=CPGLMarkerPtr( new CPGLCircleMarker( GetColor(), GetColor(), 3, true) );
			m_pMarker->SetContoured(false);
		}
		else if (sv==_T("circle"))
			m_pMarker=CPGLMarkerPtr( new CPGLCircleMarker( GetColor(), GetColor(), 4, false) );
		else if (sv==_T("triangle"))
			m_pMarker=CPGLMarkerPtr( new CPGLCircPolygonMarker( GetColor(), GetColor(), 4, false, 3) );
		else if (sv==_T("square"))
			m_pMarker=CPGLMarkerPtr( new CPGLCircPolygonMarker( GetColor(), GetColor(), 4, false,4) );
		else if (sv==_T("pentagon"))
			m_pMarker=CPGLMarkerPtr( new CPGLCircPolygonMarker( GetColor(), GetColor(), 4, false,5) );
		else if (sv==_T("hexagon"))
			m_pMarker=CPGLMarkerPtr( new CPGLCircPolygonMarker( GetColor(), GetColor(), 4, false,6) );
		else if (sv==_T("heptagon"))
			m_pMarker=CPGLMarkerPtr( new CPGLCircPolygonMarker( GetColor(), GetColor(), 4, false,7) );
		else if (sv==_T("octogon"))
			m_pMarker=CPGLMarkerPtr( new CPGLCircPolygonMarker( GetColor(), GetColor(), 4, false,8) );
		else if (sv==_T("string"))
			m_pMarker=CPGLMarkerPtr( new CPGLCharMarker(GetColor(),GetColor()) );
		else if (sv==_T("custom"))
			m_pMarker=CPGLMarkerPtr( new CPGLPolygonMarker( GetColor(), GetColor()) );
		return true;
	case LinePropOpen:
		SetOpen( !(sv == _T("false")|| sv==_T("off")) );
		return true;
	case LinePropContour:
		SetContoured( !(sv == _T("false") || sv==_T("off") ));
		return true;
	case LinePropSnapBottom:
		SetSnapBottom( !(sv == _T("false")  || sv==_T("off") ) );
		return true;
	case LinePropFilled:
		SetFilled( !(sv == _T("false")|| sv==_T("off")) );
		return true;
	case LinePropShadow:
		SetShadowing( !(sv == _T("false")|| sv==_T("off")) );
		return true;
	case LinePropInterpolation:
		if( sv==_T("linear"))
			SetInterpolationType( InterpolationTypeLinear );
		else if( sv==_T("step"))
			SetInterpolationType( InterpolationTypeStep );
		else if( sv==_T("spline"))
			SetInterpolationType( InterpolationTypeSpline );
		else if (sv==_T("multistrip"))
			SetInterpolationType( InterpolationTypeSegment );
		return true;
	case LinePropStripSize:
		if( _stscanf( sv, _T("%d"), &u ) )
			SetStripSize(u); 
		return true;
	case LinePropTension:
		if( _stscanf( sv, _T("%g"), &f ) )
			SetTension(f); 
		return true;
	default:
		return false;
	};
}

bool CPGLLine::Get( LPCTSTR szProp, CString& str)
{
	if(CPGLObject::Get(szProp, str))
		return true;

	switch( StringToLineProp( szProp ))
	{
	case LinePropWidth:
		str.Format(_T("%g"), GetLineWidth());
		return true;
	case LinePropDash:
		str=GetDashPattern();
		return true;
	case LinePropMarker:
		if (m_pMarker)
			str.Format(_T("%d"),m_pMarker->GetID());
		else
			str=_T("0");
		return true;
	case LinePropOpen:
		str=(IsOpen()) ? _T("true") : _T("false");
		return true;
	case LinePropContour:
		str=(IsContoured()) ? _T("true") : _T("false");
		return true;
	case LinePropSnapBottom:
		str=(IsSnapBottom()) ? _T("true") : _T("false");
		return true;
	case LinePropFilled:
		str=(IsFilled()) ? _T("true") : _T("false");
		return true;
	case LinePropShadow:
		str=(IsShadowing()) ? _T("true") : _T("false");
		return true;
	case LinePropInterpolation:
		switch(GetInterpolationType())
		{
		case InterpolationTypeLinear:
			str=_T("linear");
			break;
		case InterpolationTypeStep:
			str=_T("step");
			break;
		case InterpolationTypeSpline:
			str=_T("spline");
			break;
		case InterpolationTypeSegment:
			str=_T("multistrip");
			break;
		};
		return true;
	case LinePropStripSize:
		str.Format(_T("%d"), GetStripSize());
		return true;
	case LinePropTension:
		str.Format(_T("%g"), GetTension());
		return true;
	default:
		return false;
	};
}

